using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace NamedPipeServer
{
    public class NamedPipeServerManager
    {
        private readonly string _pipeName;
        private readonly int _maxConnections;
        private bool _isRunning;
        private CancellationTokenSource _cancellationTokenSource;

        public event Action<string> OnMessageReceived;
        public event Action<string> OnClientConnected;
        public event Action<string> OnClientDisconnected;
        public event Action<string> OnError;
        public event Action<string> OnInfo;

        public bool IsRunning => _isRunning;
        public string PipeName => _pipeName;

        public NamedPipeServerManager(string pipeName = "UnityWpfPipe", int maxConnections = 10)
        {
            _pipeName = pipeName;
            _maxConnections = maxConnections;
            _isRunning = false;
        }

        public async Task StartAsync()
        {
            if (_isRunning)
            {
                OnInfo?.Invoke("Server is already running");
                return;
            }

            _isRunning = true;
            _cancellationTokenSource = new CancellationTokenSource();

            OnInfo?.Invoke($"Starting Named Pipe Server: {_pipeName}");
            OnInfo?.Invoke($"Max Connections: {_maxConnections}");

            // Start multiple server instances to handle concurrent connections
            var tasks = new Task[_maxConnections];
            for (int i = 0; i < _maxConnections; i++)
            {
                int serverIndex = i;
                tasks[i] = Task.Run(() => ServerWorker(serverIndex, _cancellationTokenSource.Token));
            }

            try
            {
                await Task.WhenAll(tasks);
            }
            catch (OperationCanceledException)
            {
                OnInfo?.Invoke("Server stopped");
            }
            catch (Exception ex)
            {
                OnError?.Invoke($"Server error: {ex.Message}");
            }
            finally
            {
                _isRunning = false;
            }
        }

        public void Stop()
        {
            if (!_isRunning)
            {
                OnInfo?.Invoke("Server is not running");
                return;
            }

            OnInfo?.Invoke("Stopping server...");
            _cancellationTokenSource?.Cancel();
        }

        private async Task ServerWorker(int serverIndex, CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                NamedPipeServerStream pipeServer = null;
                StreamReader reader = null;
                StreamWriter writer = null;
                string clientId = null;

                try
                {
                    // Create a new pipe server instance
                    pipeServer = new NamedPipeServerStream(
                        _pipeName,
                        PipeDirection.InOut,
                        _maxConnections,
                        PipeTransmissionMode.Byte,
                        PipeOptions.Asynchronous
                    );

                    OnInfo?.Invoke($"Server #{serverIndex} waiting for client connection...");

                    // Wait for client connection
                    await pipeServer.WaitForConnectionAsync(cancellationToken);

                    clientId = $"Client-{DateTime.Now:HHmmss}-{serverIndex}";
                    OnClientConnected?.Invoke($"{clientId} connected to server #{serverIndex}");

                    // Create reader and writer
                    reader = new StreamReader(pipeServer, Encoding.UTF8);
                    writer = new StreamWriter(pipeServer, Encoding.UTF8) { AutoFlush = true };

                    // Send welcome message
                    await writer.WriteLineAsync($"Welcome! Connected to server #{serverIndex}");

                    // Handle client communication
                    await HandleClientCommunication(reader, writer, clientId, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    // Expected when stopping server
                    break;
                }
                catch (IOException ex)
                {
                    OnError?.Invoke($"IO Error on server #{serverIndex}: {ex.Message}");
                }
                catch (Exception ex)
                {
                    OnError?.Invoke($"Error on server #{serverIndex}: {ex.Message}");
                }
                finally
                {
                    // Clean up resources
                    try
                    {
                        if (clientId != null)
                        {
                            OnClientDisconnected?.Invoke($"{clientId} disconnected from server #{serverIndex}");
                        }

                        reader?.Dispose();
                        writer?.Dispose();
                        pipeServer?.Dispose();
                    }
                    catch (Exception ex)
                    {
                        OnError?.Invoke($"Cleanup error on server #{serverIndex}: {ex.Message}");
                    }

                    // Small delay before accepting new connections
                    if (!cancellationToken.IsCancellationRequested)
                    {
                        await Task.Delay(100, cancellationToken);
                    }
                }
            }
        }

        private async Task HandleClientCommunication(
            StreamReader reader, 
            StreamWriter writer, 
            string clientId, 
            CancellationToken cancellationToken)
        {
            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    // Read message from client
                    string message = await reader.ReadLineAsync();
                    
                    if (message == null)
                    {
                        // Client disconnected
                        break;
                    }

                    OnMessageReceived?.Invoke($"[{clientId}] {message}");

                    // Echo response back to client
                    string response = $"Echo: {message} (received at {DateTime.Now:HH:mm:ss})";
                    await writer.WriteLineAsync(response);

                    // Handle special commands
                    if (message.Equals("ping", StringComparison.OrdinalIgnoreCase))
                    {
                        await writer.WriteLineAsync("pong");
                    }
                    else if (message.Equals("time", StringComparison.OrdinalIgnoreCase))
                    {
                        await writer.WriteLineAsync($"Server time: {DateTime.Now}");
                    }
                    else if (message.Equals("disconnect", StringComparison.OrdinalIgnoreCase))
                    {
                        await writer.WriteLineAsync("Goodbye!");
                        break;
                    }
                }
            }
            catch (IOException)
            {
                // Client disconnected unexpectedly
            }
            catch (Exception ex)
            {
                OnError?.Invoke($"Communication error with {clientId}: {ex.Message}");
            }
        }

        public void Dispose()
        {
            Stop();
            _cancellationTokenSource?.Dispose();
        }
    }

    // For Unity Integration
    public class UnityNamedPipeServer : IDisposable
    {
        private NamedPipeServerManager _server;
        private Task _serverTask;

        public event Action<string> OnMessageReceived;
        public bool IsConnected { get; private set; }

        public void StartServer(string pipeName = "UnityWpfPipe")
        {
            if (_server != null)
            {
                Console.WriteLine("Server already running"); // Replace with UnityEngine.Debug.LogWarning in Unity
                return;
            }

            _server = new NamedPipeServerManager(pipeName, 3);

            _server.OnInfo += message => Console.WriteLine($"[Pipe Server] {message}");
            _server.OnError += message => Console.WriteLine($"[Pipe Server ERROR] {message}");
            _server.OnClientConnected += message => 
            {
                Console.WriteLine($"[Pipe Server] {message}");
                IsConnected = true;
            };
            _server.OnClientDisconnected += message => 
            {
                Console.WriteLine($"[Pipe Server] {message}");
                IsConnected = false;
            };
            _server.OnMessageReceived += message => 
            {
                Console.WriteLine($"[Pipe Server] Received: {message}");
                OnMessageReceived?.Invoke(message);
            };

            _serverTask = _server.StartAsync();
            Console.WriteLine("Unity Named Pipe Server started");
        }

        public void StopServer()
        {
            _server?.Stop();
            _server = null;
            IsConnected = false;
            Console.WriteLine("Unity Named Pipe Server stopped");
        }

        public void Dispose()
        {
            StopServer();
            _server?.Dispose();
        }
    }
}
